package za.ac.sun.cs.viz.distrib;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;

import org.abego.treelayout.TreeForTreeLayout;

public class VizTreeManager {

	private final Map<Long, Long> timestampRenaming = new HashMap<Long, Long>();

	private final Map<String, VizTreeNode> nameMap = new HashMap<String, VizTreeNode>();

	private final SortedMap<Long, String> timestampTexts = new TreeMap<Long, String>();

	private VizTreeLayout<VizNode> previousTree = null;

	private String previousText = null;

	private static final VizTreeRoot root = new VizTreeRoot();

	public VizTreeManager(String filename) {
		SortedMap<Long, List<String>> f = readFile(filename);
		long previousTimestamp = 0;
		for (Map.Entry<Long, List<String>> e : f.entrySet()) {
			long t = e.getKey();
			for (String s : e.getValue()) {
				if (t <= previousTimestamp) {
					t = previousTimestamp + 1;
				}
				timestampTexts.put(t, s);
				processLine(t, s);
				previousTimestamp = t;
			}
		}
		for (Long timestamp : timestampTexts.keySet()) {
			timestampRenaming.put((long) timestampRenaming.size(), timestamp);
		}
		// dumpNodes();
	}

	private void processLine(long timestamp, String line) {
		String[] fields = line.split("@@");
		int id = Integer.parseInt(fields[2].trim());
		String cmd = fields[3].trim();
		if (cmd.equals("NEWCOUNT")) {
			processNewCount(timestamp, id, fields, line);
		} else if (cmd.equals("CHOICE")) {
			processChoice(timestamp, id, fields, line);
		} else if (cmd.equals("UPDATE")) {
			processUpdate(timestamp, id, fields, line);
		} else if (!cmd.equals("ID")) {
			System.out.println("SHOULDN'T BE HAPPENING");
			System.exit(1);
		}
	}

	private void processNewCount(long timestamp, int id, String[] fields, String line) {
		String s = fields[4].trim();
		long v = Long.parseLong(fields[5].trim());
		VizTreeNode n = parseNode(s);
		VizTreeNode p = parseParent(s);
		n.addValue(timestamp, id, v, VizTreePane.NEW);
		p.addChild(n);
		timestampTexts.put(timestamp, line);
	}

	private void processChoice(long timestamp, int id, String[] fields, String line) {
		String s = fields[4].trim();
		VizTreeNode n = parseNode(s);
		VizTreeNode p = parseParent(s);
		long v = Long.parseLong(fields[5].trim());
		n.addValue(timestamp, id, v, VizTreePane.YES);
		p.addChild(n);
		s = fields[6].trim();
		n = parseNode(s);
		p = parseParent(s);
		v = Long.parseLong(fields[7].trim());
		n.addValue(timestamp, id, v, VizTreePane.NO);
		p.addChild(n);
		timestampTexts.put(timestamp, line);
	}

	private void processUpdate(long timestamp, int id, String[] fields, String line) {
		String s = fields[4].trim();
		if (s.equals("root")) {
			root.addValue(timestamp);
		} else {
			long v = Long.parseLong(fields[7].trim());
			VizTreeNode n = parseNode(s);
			VizTreeNode p = parseParent(s);
			n.addValue(timestamp, id, v, VizTreePane.CHANGE);
			p.addChild(n);
		}
		timestampTexts.put(timestamp, line);
	}

	private SortedMap<Long, List<String>> readFile(String filename) {
		SortedMap<Long, List<String>> content = new TreeMap<Long, List<String>>();
		try {
			FileReader f = new FileReader(new File(filename));
			BufferedReader r = new BufferedReader(f);
			String x = r.readLine();
			while (x != null) {
				if (x.startsWith("@@")) {
					String[] fields = x.split("@@");
					long t = Long.parseLong(fields[1].trim());
					String c = fields[3].trim();
					if (!c.equals("NEWVAR") && !c.equals("DONE")) {
						List<String> s = content.get(t);
						if (s == null) {
							s = new LinkedList<String>();
							content.put(t, s);
						}
						s.add(x);
					}
				}
				x = r.readLine();
			}
			r.close();
		} catch (FileNotFoundException x) {
			x.printStackTrace();
			System.exit(1);
		} catch (IOException x) {
			x.printStackTrace();
			System.exit(1);
		}
		return content;
	}

	@SuppressWarnings("unused")
	private void dumpNodes() {
		for (Entry<String, VizTreeNode> entry : nameMap.entrySet()) {
			System.out.println("-- " + entry.getKey());
			System.out.println(entry.getValue());
		}
	}

	public VizTreeNode parseNode(String name) {
		VizTreeNode n = nameMap.get(name);
		if (n == null) {
			String[] m = name.split("&&");
			n = new VizTreeNode(m[0].trim().replaceAll(" ", ""));
			nameMap.put(name, n);
		}
		return n;
	}

	public VizTreeNode parseParent(String name) {
		int i = name.indexOf("&&");
		if (i < 0) {
			return root;
		} else {
			return parseNode(name.substring(i + 2).trim());
		}
	}

	public int getTimestampCount() {
		return timestampTexts.size();
	}

	public String getText(long timestamp) {
		Long ts = timestampRenaming.get(timestamp);
		if (ts == null) {
			return previousText;
		} else {
			String t = timestampTexts.get(ts);
			if (t == null) {
				return previousText;
			} else {
				previousText = t;
				return t;
			}
		}
	}

	public TreeForTreeLayout<VizNode> getTree(long timestamp) {
		Long ts = timestampRenaming.get(timestamp);
		if (ts == null) {
			return previousTree;
		} else {
			VizNode rootVizNode = root.getVizNode(ts);
			VizTreeLayout<VizNode> tree = new VizTreeLayout<VizNode>(
					rootVizNode);
			addChildrenRecursively(ts, tree, root, rootVizNode);
			previousTree = tree;
			return tree;
		}
	}

	private void addChildrenRecursively(long timestamp,
			VizTreeLayout<VizNode> tree, VizTreeNode node, VizNode nodeViz) {
		if (node.getValue(timestamp) > 0) {
			VizTreeNode child = node.getLeftChild();
			if (child != null) {
				VizNode childViz = child.getVizNode(timestamp);
				if (childViz != null) {
					tree.addChild(nodeViz, childViz);
					addChildrenRecursively(timestamp, tree, child, childViz);
				}
			}
			child = node.getRightChild();
			if (child != null) {
				VizNode childViz = child.getVizNode(timestamp);
				if (childViz != null) {
					tree.addChild(nodeViz, childViz);
					addChildrenRecursively(timestamp, tree, child, childViz);
				}
			}
		}
	}

}
